/* 
 CALL/APPLY/BIND
  @1 基础语法和相互区别
  @2 重写内置的call方法 
  @3 获取数组最大值和最小值
  @4 把类数组转换为数组
 */

/* 
 call：立即执行某个函数，并且改变函数中的this指向
   @params
     context:需要改变函数的this指向
     params:需要传递给函数的实参值
   @ return 
     函数执行的返回值就是call执行的返回值
   call方法中的this指向即将处理执行的函数
 */
Function.prototype.call = function call(context, ...params) {
    // context->obj  params->[10,20]  this->fn
    // 目的：把fn「this」执行，让fn「this」中的this指向obj「context」，把10/20「params」传递给fn「this」，接收fn「this」执行的返回值作为call执行的返回值
    // @1 给obj设置的属性名，不能对现有obj中的属性产生影响，所以要设置一个“唯一”的属性
    // @2 关于 context 的格式处理：context如果传递的是null/undefined，我们让其变为window；如果传递的是原始值，我们让其“装箱”！
    let type,
        result,
        key = Symbol('KEY');
    if (context == null) context = window;
    type = typeof context;
    if (type !== 'object' && type !== 'function') context = Object(context);
    context[key] = this;
    result = context[key](...params);
    delete context[key];
    return result;
};


/* 
 bind：“预处理”函数中的this指向和参数
 */
Function.prototype.bind = function bind(context, ...params) {
    // context->obj  params->[10,20]  this->fn
    let self = this;
    return function proxy(ev) {
        // this->submit ev->事件对象
        params = params.concat(ev);
        return self.call(context, ...params);
    };
};
/* 
submit.onclick = function proxy(ev) {
    fn.call(obj, 10, 20, ev);
}; 
submit.onclick = fn.bind(obj, 10, 20);
----
只要执行bind方法，返回一个proxy赋值给submit.onclick即可
点击事件触发，先把proxy执行，在proxy中再把fn执行，基于call实现自己的需求即可
*/


const submit = document.querySelector('#submit');
const fn = function fn(x, y, ev) {
    console.log(this, x, y, ev);
    return x + y;
};
let obj = { name: 'obj' };
// 需求：给submit绑定点击事件，点击的时候执行fn「fn中的this指向obj，传递10/20给x/y，最后一个参数可以是：默认传递的事件对象」
// submit.onclick = fn;  //this->submit  x->事件对象  y->undefined
// submit.onclick = fn.call(obj, 10, 20); //这样处理是错误的：call会让fn立即执行，不等点击就执行了
/*
 解决办法：
  @1 把一个匿名函数(proxy)赋值给#submit的事件绑定，当点击触发，先执行这个proxy函数
  @2 在proxy执行的时候，基于call方法完成我们的需求即可
 */
/* submit.onclick = function proxy(ev) {
    fn.call(obj, 10, 20, ev);
}; */
submit.onclick = fn.bind(obj, 10, 20);


//========================
/* 
let result = fn.call(obj, 10, 20);
  @1 fn基于__proto__找到Function.prototype.call方法
  @2 把找到的call方法执行
 */
/* const fn = function fn(x, y) {
    console.log(this, x, y);
    return x + y;
};
let obj = {
    name: 'obj',
    fn: 100
};
let result = fn.call(obj, 10, 20);
console.log(result); */

/* // 如果没有call方法，我们如何实现这个需求？
// 处理方案：给obj设置一个属性，让其和函数关联在一起，接下来基于obj的成员访问去触发函数执行「注意：原本obj中不具备fn属性，所以达到需要的效果后，我们把新增的属性给删除掉」
obj.fn = fn;
let result = obj.fn(10, 20);
delete obj.fn;
console.log(result); */
